Xcode错误 - Plist和转义符「/」

前两天遇到一个 Plist 和转义符的问题,这个问题我翻了 Google 十几页,但是并没有找到解决的方法,也没有找到关于这个问题的描述,因此我想先记录下来,如果有人和我遇到同样的问题,可以知道结果是怎样的。

当然,由于我的才疏学浅,我没能解决不一定大家都不能解决,如果你有解决的思路,欢迎邮件我啊!

如果不想看过程,可以直接翻到最下面看验证的结果

问题是这样的,我创建了一个 Plist 文件,希望用来存储用户的设置选项,遇到的第一个坑是 KVC 不支持 Bool 类型,这个是我没想起了,使用 0 和 1 来替代解决了,现在要讲的是第二个坑。

因为我在项目中使用了字体图标,它类似 Emoji 表情:使用 Unicode 编码,获取特定的图标。在其他的页面中,我使用了全局变量 + UILabel 转 UIImage 的方式,获取并使用字体图标,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
let settingIconCode = "\u{e702}"
class func image(withIconCode iconCode: String, fontName: String, size: CGFloat, color: UIColor?) -> UIImage? {
let imageSize = CGSize(width: size, height: size)
UIGraphicsBeginImageContextWithOptions(imageSize, false, UIScreen.main.scale)
let label = UILabel(frame: CGRect(x: 0, y: 0, width: size, height: size))
label.font = UIFont(name: fontName, size: size)
label.text = iconCode
if (color != nil) {
label.textColor = color
}
else {
label.textColor = UIColor.white
}
label.layer.render(in: UIGraphicsGetCurrentContext()!)
if let image = UIGraphicsGetImageFromCurrentImageContext() {
return image
}
else {
print("绘制失败")
}
return nil
}

当我写到设置页面时,决定用 Plist 作为本地化存储的方案,因此我创建了一个 Plist 文件,然后每个 Item 包含以下 4 个属性:

1
2
3
4
5
6
7
8
/// 设置项名称
var name: String!
/// 设置项图标
var iconCode: String!
/// 是否显示 Switch 控件
var switchHide: String!
/// 设置项状态
var state: String!

讲道理这时我已经可以转模型然后在 Cell 里面使用了,但是在我使用上面写的方法:

1
class func image(withIconCode iconCode: String, fontName: String, size: CGFloat, color: UIColor?) -> UIImage?

发现这个方法失效了,返回的是 图片,而不是应该获取的字体图标,这个省略号自然是因为超出 UILabel 的显示范围形成的,下面为了叙述方便,我用 \u{e702} 来代入。

之前我的全局变量 settingIconCode ,传入到 imageWithIconCode 方法时没有问题,但是将 \u{e702} 写在 Plist 中赋值给 iconCode,再转为模型后出错,于是我打印了 iconCode 参数,结果和我预期是一样的 \u{e702},然后我将 iconCode\u{e702} 一起打印,输出结果如下:

1
print("\u{e702}", iconCode) //? \u{e702}

这说明两者虽然都是 String 类型,Value 看起来也一样,但是并不是同一个东西,于是我想到会不会是因为 \u{e702} 是经过转换的 String,而 iconCode 是单纯的 String?

我们知道,\u{e702} 等同于 U+\e702 ,也就是 16 进制的 e702 ,因此我在 Plist 中,将 iconCode 修改为 16 进制的 0x1E702 ,然后我通过 \u{\(iconCode)} 进行转换,但是出错了, 这里的{} 中不允许使用 \(),因此不能使用字符串拼接

于是我放弃了这个方法,回想是不是对象是不是被修改了,因此我找到最初读取到 Plist 的地方,打印获取的 Plist 数组:

1
2
let settingDicts = NSArray(contentsOfFile: Bundle.main.path(forResource: "Setting.plist", ofType: nil)!)
print(settingDicts?.firstObject)

我得到的结果是:

1
2
3
4
5
6
Optional({
iconCode = "\\u{e702}";
name = "\U81ea\U52a8\U5b9a\U4f4d\U5230\U6700\U8fd1\U7ad9\U70b9";
state = 0;
switchHide = 1;
})

从这里可以看到,打印结果多出了一个 \,因此我猜想,Plist 并不能理解转义符,因此将 \ 当做字符来处理,所以 \ = \\,为了验证这个猜想,我又修改了 Value 并打印,得到的结果如下:

1
2
3
iconCode = \(iconCode) //iconCode = "\\(iconCode)";
iconCode = (iconCode) //iconCode = "(iconCode)";
iconCode = \\(iconCode) //iconCode = "\\\\(iconCode)";

因此我们可以得出结论,Plist 并不能理解转义符,因此会将 \ 当做字符来处理,而不是当做转义符来处理